package com.listeningframework.boot.codegen.lang;

import com.listeningframework.boot.codegen.CodeGenConfiguration;
import com.listeningframework.boot.codegen.CodeGenEntity;
import com.listeningframework.boot.codegen.CodeGenORM;
import com.listeningframework.boot.codegen.context.SystemContext;
import com.listeningframework.boot.codegen.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.Entity;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

class EntityScannerUtils {

    private EntityScannerUtils() {
    }

    static final EntityScannerUtils newinstance() {
        return new EntityScannerUtils();
    }

    /**
     * 扫描CodeGen对象Entity
     *
     * @return Set<Class<?>>
     * @throws BusinessException 
     */
    Set<Class<?>> codeGenEntityList() throws BusinessException {
        String classPackage = SystemContext.getClassPath();
        CodeGenConfiguration config = SystemContext.getCodeGenConfig();
        return findEntity(classPackage, CodeGenORM.isJPA(config.orm()));
    }

    private final Set<Class<?>> findEntity(String classpackage, boolean isJPA) throws BusinessException {
        try {
            File rootDir = new File(classpackage);
            if (!rootDir.exists()) {
                return Collections.emptySet();
            }
            if (!rootDir.isDirectory()) {
                return Collections.emptySet();
            }
            if (!rootDir.canRead()) {
                return Collections.emptySet();
            }
            Set<Class<?>> result = new LinkedHashSet<Class<?>>(8);
            findEntity(classpackage, rootDir, new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    if (dir.isDirectory()) {
                        return true;
                    }
                    if (StringUtils.endsWith(name, ".class")) {
                        return true;
                    }
                    return false;
                }
            }, isJPA, result);
            return result;
        } catch (Exception e) {
            throw new BusinessException("findAnnotated Class Fail", e);
        }
    }

    /**
     * 递归加载文件
     */
    private final void findEntity(String classPackage, File directory, FilenameFilter filter, boolean isJPA,
                                  Set<Class<?>> result) throws IOException {
        File[] dirContents = null;
        if (filter == null) {
            dirContents = directory.listFiles();
        } else {
            dirContents = directory.listFiles(filter);
        }
        if (dirContents == null) {
            return;
        }
        Arrays.sort(dirContents);
        for (File content : dirContents) {
            if (content.isDirectory()) {
                findEntity(classPackage, content, filter, isJPA, result);
            } else {
                validateEntityClass(classPackage, content, isJPA, result);
            }
        }
    }

    private final void validateEntityClass(String classPath, File file, boolean isJPA, Set<Class<?>> result) {
        String filepath = file.getAbsolutePath();
        String treatClassPath = classPath.replace("\\", File.separator);
        treatClassPath = treatClassPath.substring(classPath.indexOf(File.separator));
        String treatFilePath = filepath.replace("\\", File.separator);
        treatFilePath = treatFilePath.substring(classPath.indexOf(File.separator));
        treatFilePath = treatFilePath.replace(treatClassPath, "");
        treatFilePath = treatFilePath.substring(0, treatFilePath.lastIndexOf("."));
        treatFilePath = treatFilePath.replace(File.separator, ".");
        Class<?> clz = addEntityClass(treatFilePath, isJPA);
        if (clz != null) {
            result.add(clz);
        }
    }

    private Class<?> addEntityClass(String className, boolean isJPA) {
        Class<?> clz = null;
        try {
            clz = SystemContext.getClassLoader().loadClass(className);
        } catch (Exception e) {
            return null;
        }
        if (clz == null || !clz.isAnnotationPresent(CodeGenEntity.class)) {
            return null;
        }

        if (isJPA) {
            return clz.isAnnotationPresent(Entity.class) ? clz : null;
        } else {
            return clz;
        }
    }
}
